//**************************************************************************
// Rs495.c
//                      RS485 network protocol
//
// Version 1.0 beta
//
// Processor: PIC16F87x
//
// Network packet protocol:
//
// STX ( 0x02 )
// NET ADDRESS HIGH  
// NET ADDRESS LOW
// PAYLOAD LENGTH
// COMMAND
// DATA ( Optional )
// CRC HIGH
// CRC LOW
//
//
//
//
//
// By: J.Winpenny
// j.winpenny@ukonline.co.uk
//
//**************************************************************************

#include "Generic.h"
#include "OSA.h"
#include "service/osa_flag.h"
#include "Rs485.h"
#include "delay.h"
#include "include\rs485.h"




OST_FLAG Rs485Flag;

OST_MSG drv_rs485_msg_rx;
OST_MSG drv_rs485_msg_tx;
OST_QMSG DRV_rs485_qmsg_rx;
OST_QMSG DRV_rs485_qmsg_tx;
OST_MSG DRV_rs485_msg_q_tx[RS_SIZE_OF_MSGQ];
OST_MSG DRV_rs485_msg_q_rx[RS_SIZE_OF_MSGQ];
char  strPacket[72];


extern int TX_ByteNumber;
extern int RX_ByteNumber;
extern int TX_len;
extern int RX_len;

//****************************************************************************
// void Drv_Rs485(void)
//
// task for rs485
//****************************************************************************

void Drv_Rs485(void) {
    static TxCommPack *txpack;
   
    OS_WaitAnyFlag(Rs485Flag, 0xff);
    if OS_CheckAnyFlag(Rs485Flag, SER_REC) {
        //        OS_WaitQMsg (RS_qmsg_rx, rs_msg_rx);
        //FIXME finire
    }
    if OS_CheckAnyFlag(Rs485Flag, SER_TX) {
        while (OS_CheckQMsg(DRV_rs485_qmsg_tx)) {
            OS_WaitQMsg(DRV_rs485_qmsg_tx, drv_rs485_msg_tx);
            txpack = (TxCommPack *) drv_rs485_msg_tx;
            TX_len = Rs485BuildPacket(strPacket, txpack);

            if (TX_len > 0) {// FIXME necessario?

                if (IEC0bits.U1TXIE == 0) {
                    U1STAbits.UTXEN = 1;
                    while (U1STAbits.UTXBF);
                    U1TXREG =  strPacket[TX_ByteNumber++];
                    IEC0bits.U1TXIE = 1;
                }//(IEC0bits.U1TXIE == 0)
            }//if (tx_len > 0)


        }
    }
    OS_ClearFlag(Rs485Flag, 0xff);

}

//****************************************************************************
// void Rs485Initialise(void)
//
// Initialise RS485 network driver
//****************************************************************************

void Rs485Initialise(WORD_VAL cAddr, CommProt *cmptr) {
    cmptr->cOurAddr = cAddr;
    cmptr->cRS485State = PKT_WAIT_START;
    OS_CreateFlag(Rs485Flag);
    TX_ByteNumber = 0;
    RX_ByteNumber = 0;
}


//****************************************************************************
// char PacketForUs(void)
//
// Decide if packet valid and destined for this node.
// Ignore invalid packets and packets for other nodes
//
//****************************************************************************

static BOOL Rs485Process(RxCommPack *rxpack, CommProt *commprot) {
    char cOurPkt, cPktReady;

    cOurPkt = FALSE;
    cPktReady = FALSE;


    if (commprot->cRS485State == PKT_COMPLETE) {
        /* State Machine Received the packet                            */
        /* (Invalid) and destined for this node                         */
        if ((rxpack->cNetAddr).Val == (commprot->cOurAddr).Val) {
            cOurPkt = TRUE;
        } else {
            /*                                                             */
            /*   ClearLine2();                                             */
            /*   LcdWrite("traffic");   Network traffic for other nodes    */
            /*delay_ms(200);                                               */
        }

        /* Validate packet CRC                                              */
        commprot->cRS485State = PostValidatePacket(rxpack);

        if ((commprot->cRS485State == PKT_INVALID) || (commprot->cRS485State == PKT_VALID)) {
            // Reject invalid packets
            if (commprot->cRS485State == PKT_INVALID) // NAK our invalid packets
            {
                //ClearLine2();
                if (commprot->cError == BAD_CRC); //LcdWrite("Bad CRC");
                else if (commprot->cError == BAD_LENGTH)
                    Nop(); //LcdWrite("Bad length");
                /* FIXME    what are we going to do if something is wrong   */
                Delay_Us(2000); 
                //if ( cOurPkt ) Rs485SendPacket( SENSOR_NAK, 0, NULL );//FIXME CHECK
                commprot->cRS485State = PKT_WAIT_START;
            } else if (commprot->cRS485State == PKT_VALID) // If packet valid
            { // and destined for this node
                if (cOurPkt) cPktReady = TRUE;
                else commprot->cRS485State = PKT_WAIT_START;
            }
        }
    }


    return cPktReady;

}


//****************************************************************************
// void Rs485Decode(CommProt *cmptr)
//
// Decode an incomming packet on the RS485 network
//
// Expecting:
// START,
// NETWORK ADDRESS_HIGH,
// NETWORK ADDRESS_LOW,
// PAYLOAD LENGTH,
// COMMAND,
// optional DATA,
// CRC HIGH,
// CRC LOW
//
//****************************************************************************

char Rs485Decode(RxCommPack *cmptr, CommProt *commprot, char rxChar) {

    switch (commprot->cRS485State) {
        case PKT_WAIT_START: cmptr->cStart = rxChar;
            if (rxChar == PKT_START) // Check for the start of packet byte
            {
                commprot->cRS485State++;
            }
            break;

        case PKT_WAIT_ADDR_HIGH: MSB(cmptr->cNetAddr) = rxChar;
            commprot->cRS485State++;
            break;

        case PKT_WAIT_ADDR_LOW: LSB(cmptr->cNetAddr) = rxChar;
            commprot->cRS485State++;
            break;

        case PKT_WAIT_LEN: cmptr->cLenExpected = rxChar;
            if (cmptr->cLenExpected > sizeof (cmptr->c485Buf)) {
                commprot->cRS485State = PKT_INVALID;
                commprot->cError = BAD_LENGTH;
            } else {
                commprot->cBufPtr = 0;
                commprot->cRS485State++;
            }
            break;

        case PKT_CMD: cmptr->cCommand = rxChar;
            if (PacketHasPayload()) commprot->cRS485State = PKT_WAIT_DATA;
            else commprot->cRS485State = PKT_WAIT_CRC_HIGH;
            break;

        case PKT_WAIT_DATA: cmptr->c485Buf[(int) commprot->cBufPtr] = rxChar;
            commprot->cBufPtr++;
            if (commprot->cBufPtr == cmptr->cLenExpected) // If last byte of data received
            {
                commprot->cRS485State++; // next byet is the CRC high byte
            }
            break;

        case PKT_WAIT_CRC_HIGH: MSB(cmptr->cRxCrc) = rxChar;
            commprot->cRS485State++;
            break;

        case PKT_WAIT_CRC_LOW: LSB(cmptr->cRxCrc) = rxChar;
            commprot->cRS485State = PKT_COMPLETE;
            break;

        case PKT_COMPLETE: break; // Idle state
        case PKT_VALID: break; // Idle state
        case PKT_INVALID: break; // Idle state

        default: commprot->cRS485State = PKT_WAIT_START;
            break;
    }

    return commprot->cRS485State;
}


//****************************************************************************
// void Rs485BuildPacket( char cAddr, char cCmd, char cLen, char *cData )
//
// Send a packet over the RS485 link
//
// Input: TxCommPack packet
//
//****************************************************************************

//void Rs485SendPacket(char cCmd, char cLen, char *cData, TxCommPack *cmptr) {

int Rs485BuildPacket(char *str, TxCommPack *cmptr) {
    short int i, j;
    char c, d;
    //char *str;
    char szof;
    j = 0;
    /* TxCommPack cmptr rappresenta un pacchetto gi� costruito a cui manca solo CRC */


    /* FIXME CRC */
    (cmptr->cCalcCrc).Val = 0xffff; // Clear CRC

    // Send some NULL preamblesfopr receiving UART
 //   for (c = 0; c < NUM_TX_PREAMBLE; c++) Rs485SendChar(0x00);

    /* FIXME E' sbagliato   */
    szof = sizeof (TxCommPack);

//    str = calloc(1,szof);

    for (i = 0; i < szof; i++) {
        str[i] = 0x00;
    }

    str[j++] = cmptr->cStart;
    str[j++] = MSB(cmptr->cNetAddr);
    str[j++] = LSB(cmptr->cNetAddr);
    str[j++] = cmptr->cCommand;
    str[j++] = cmptr->cLenExpected;
    for (i = 0; i < cmptr->cLenExpected; i++) {
        str[j++] = cmptr->c485Buf[i];
    }
    str[j++] = MSB(cmptr->cCalcCrc);
    str[j++] = LSB(cmptr->cCalcCrc);

    //FIXME
    str[j++] = '\0';

    return j;
}


//****************************************************************************
// void Rs485GetPacket( char *cCommand, char cLen, char *cData )
//
// Pass packet to main application
//
//****************************************************************************

void Rs485GetPacket(char *cCom, char *cLen, char *cData, RxCommPack *cmptr) {
    char c;

    *cCom = cmptr->cCommand;
    *cLen = cmptr->cLenExpected;
    for (c = 0; c < cmptr->cLenExpected; c++) cData[(int) c] = cmptr->c485Buf[(int) c];

    cData[(int) cmptr->cLenExpected] = 0x00; // Termninate
}

/*
 * Process one Message Byte to update the current CRC Value
 */
void Rs485UpdateCrc(char cVal) {
    /*
            CRC16_Update4Bits( cVal >> 4 );		// High nibble first
            CRC16_Update4Bits( cVal & 0x0F );	// Low nibble
     */
}

//****************************************************************************
// void Rs485SendChar( char c )
//
// Driver level of RS485 protocol
// Output character on RS485 driver
// // Include line turn around time
//****************************************************************************

void Rs485SendChar(char c) {
}


//****************************************************************************
// char PostValidatePacket(void)
//
// Verify the CRC on the last packet received
//
// Check if the CRC is correct
// and return the updated state as the result
//
//****************************************************************************

char PostValidatePacket(RxCommPack *commprt) {
    char c, d;

    //  CRC16_Init();
    Rs485UpdateCrc(PKT_START);
    /*
      Rs485UpdateCrc(cNetAddrHigh);
      Rs485UpdateCrc(cNetAddrLow);
      Rs485UpdateCrc(cLenExpected);
      Rs485UpdateCrc(cCommand);

      if ( PacketHasPayload() )  // If the packet has a payload,
      {                          // then include the data in the CRC.
         for ( c = 0; c < cLenExpected; c++ )
         {
            d = c485Buf[c];
            Rs485UpdateCrc( d );
         }
      }
                                 // Check if the CRC is correct
                                 // and return the updated state as the result
      if ( (cRxCrcHigh == cCalcCrcHigh)&&(cRxCrcLow == cCalcCrcLow) )
      {
         cRS485State = PKT_VALID;
      }
      else
      {
         cError = BAD_CRC;
         cRS485State = PKT_INVALID;
             ClearLine2();
         BinToHexAscii(cRxCrcHigh );
         BinToHexAscii(cRxCrcLow );
         LcdWrite(' ');
         BinToHexAscii(cCalcCrcHigh);
         BinToHexAscii(cCalcCrcHigh);
         delay_ms(255);
         delay_ms(255);
         delay_ms(255);
         delay_ms(255);
      }

      return cRS485State;
     */
}

//****************************************************************************
// char GetPacketCmdType(void)
//
// Check packet command type
// Return TRUE if packet has a data payload.
//
//****************************************************************************

char PacketHasPayload(void) {
    /*
      if ( cCommand == SENSOR_GET_DATA ) return TRUE;
      else return FALSE;
     */
}

//****************************************************************************
// void BinToHexAscii( char c )
//
// Contributed by: Nick De Smith
//
//****************************************************************************

void BinToHexAscii(char c) {
    const char hexMap[17] = "0123456789ABCDEF";
    /*
        LcdWrite( hexMap[(c >> 4) & 0xF] );
        LcdWrite( hexMap[(c & 0xF)] );
     */
}

